/*
 * selekcja.cpp
 *
 * Copyright (C) 2008, 2009
 *
 * Lukasz Bieniasz-Krzywiec
 * Dariusz Leniowski
 */

#include "selekcja.h"

#include "osobnik.h"
#include "fabryka.h"

extern real     gra[2][2];
extern int      *RODZAJE;
extern int      RODZAJE_REF[MAX_RODZAJOW+1];

extern osobnik  **populacja;
extern int      *osobnik_fitness;
extern int      *fitness_osobnik;

extern int wez_losowego_sasiada(int);

/* SELEKCJA PROSTA */

selekcja_prosta::selekcja_prosta() : prawd_bledu(0.0) {}
selekcja_prosta::selekcja_prosta(real _prawd_bledu) : prawd_bledu(_prawd_bledu) {}
selekcja_prosta::~selekcja_prosta() {}

void selekcja_prosta::wykonaj()
{
  if (r->rand() < prawd_bledu) {
    fabryka::nowy(RODZAJE[r->rand(RODZAJOW)], r->rand(N));
  }	else {
    int numer = fitness_osobnik[N-1]; // znajdz numer najgorszego osobnika
    fabryka::nowy(populacja[fitness_osobnik[0]]->rodzaj, numer); // wstaw na to miejsce nowego o rodzaju najlepszego osobnika
  }
}

/* SELEKCJA IMITACJA */

selekcja_imitacja::selekcja_imitacja(): prawd_bledu(0.0) {}
selekcja_imitacja::selekcja_imitacja(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja::~selekcja_imitacja() {}

void selekcja_imitacja::wykonaj()
{
  int wierzcholek, sasiad;

  wierzcholek = r->rand(N);

  if (r->rand() < prawd_bledu) {
    fabryka::nowy(RODZAJE[r->rand(RODZAJOW)], wierzcholek);
  } else {
    sasiad = wez_losowego_sasiada(wierzcholek);

    if (sasiad != -1 && populacja[sasiad]->fitness > populacja[wierzcholek]->fitness) {
      fabryka::nowy(populacja[sasiad]->rodzaj, wierzcholek);
    }
  }
}

/* SELEKCJA IMITACJA WSZYSTKIE */

selekcja_imitacja_wszystkie::selekcja_imitacja_wszystkie(): prawd_bledu(0.0) {}
selekcja_imitacja_wszystkie::selekcja_imitacja_wszystkie(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_wszystkie::~selekcja_imitacja_wszystkie() {}

void selekcja_imitacja_wszystkie::wykonaj()
{
  int wierzcholek, sasiad;
  vector<int> nowy_rodzaj(N);

#pragma omp parallel for private(wierzcholek, sasiad)
  FOREN(wierzcholek) {
    if (r->rand() < prawd_bledu) {
      nowy_rodzaj[wierzcholek] = RODZAJE[r->rand(RODZAJOW)];
    } else {
      sasiad = wez_losowego_sasiada(wierzcholek);

      if (sasiad != -1 && populacja[sasiad]->fitness > populacja[wierzcholek]->fitness) {
        nowy_rodzaj[wierzcholek] = populacja[sasiad]->rodzaj;
      } else {
        nowy_rodzaj[wierzcholek] = -1;
      }
    }
  }

  FOREN(wierzcholek) {
    if (nowy_rodzaj[wierzcholek] != -1) {
      fabryka::nowy(nowy_rodzaj[wierzcholek], wierzcholek);
    }
  }
}

/* SELEKCJA PROCES MORANA */

selekcja_proces_morana::selekcja_proces_morana() {}
selekcja_proces_morana::~selekcja_proces_morana() {}

void selekcja_proces_morana::wykonaj()
{
  int   x, y;
  real  suma = 0.0, p;

#pragma omp parallel for private(x) reduction(+:suma)
  FOREN(x) {
    suma += populacja[x]->fitness;
  }

  p = r->rand() * suma;

  x = -1; suma = 0.0;
  while (suma < p) {
    suma += populacja[++x]->fitness;
  }

  y = wez_losowego_sasiada(x);

  if (y != -1) {
    fabryka::nowy(populacja[x]->rodzaj, y);
  }
}

/* SELEKCJA PROCES MORANA ARTYKUL */

selekcja_proces_morana_artykul::selekcja_proces_morana_artykul(): prawd_bledu(0.0) {}
selekcja_proces_morana_artykul::selekcja_proces_morana_artykul(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_proces_morana_artykul::~selekcja_proces_morana_artykul() {}

void selekcja_proces_morana_artykul::wykonaj()
{
  int x = r->rand(N);

  if (r->rand() < prawd_bledu) {
    fabryka::nowy(RODZAJE[r->rand(RODZAJOW)], x);
  } else {
    int y = wez_losowego_sasiada(x);

    if (y != -1) {
      real Px = populacja[x]->fitness;
      real Py = populacja[y]->fitness;
      real D = gra[1][0] - gra[0][1]; /* TODO - w SnowDrift inaczej */

      real k = max(populacja[x]->stopien_wierzcholka(), populacja[y]->stopien_wierzcholka());
      if (r->rand() < (Py - Px) / (D * k)) {
        fabryka::nowy(populacja[y]->rodzaj, x);
      }
    }
  }
}

/* SELEKCJA PROCES MORANA ARTYKUL WSZYSTKIE */

selekcja_proces_morana_wszystkie_artykul::selekcja_proces_morana_wszystkie_artykul(): prawd_bledu(0.0) {}
selekcja_proces_morana_wszystkie_artykul::selekcja_proces_morana_wszystkie_artykul(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_proces_morana_wszystkie_artykul::~selekcja_proces_morana_wszystkie_artykul() {}

void selekcja_proces_morana_wszystkie_artykul::wykonaj()
{
  int         x;
  vector<int> nowy_rodzaj(N);

#pragma omp parallel for private(x)
  FOREN(x) {
    if (r->rand() < prawd_bledu) {
      nowy_rodzaj[x] = RODZAJE[r->rand(RODZAJOW)];
    } else {
      int y = wez_losowego_sasiada(x);

      if (y != -1) {
        real Px = populacja[x]->fitness;
        real Py = populacja[y]->fitness;
        real D = gra[1][0] - gra[0][1]; /* TODO - w SnowDrift inaczej */
        real k = max(populacja[x]->stopien_wierzcholka(), populacja[y]->stopien_wierzcholka());

        if (r->rand() < (Py - Px) / (D * k)) {
          nowy_rodzaj[x] = populacja[y]->rodzaj;
        } else {
          nowy_rodzaj[x] = -1;
        }
      } else {
        nowy_rodzaj[x] = -1;
      }
    }
  }

  FOREN(x) {
    if (nowy_rodzaj[x] != -1) {
      fabryka::nowy(nowy_rodzaj[x], x);
    }
  }
}

/* SELEKCJA RODZAJOW */

selekcja_imitacja_rodzajow::selekcja_imitacja_rodzajow(): prawd_bledu(0.0) {}
selekcja_imitacja_rodzajow::selekcja_imitacja_rodzajow(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_rodzajow::~selekcja_imitacja_rodzajow() {}

void selekcja_imitacja_rodzajow::wykonaj()
{
  int x = r->rand(N), m;

  if (r->rand() < prawd_bledu) {
    m = r->rand(RODZAJOW);
  } else {
    int         y, i;
    vector<real> suma(RODZAJOW, 0.0);

    suma[RODZAJE_REF[populacja[x]->rodzaj]] += populacja[x]->fitness;
#pragma omp parallel for private(y)
    FOREN(y) {
      real k = f->oblicz(x, y);
      if (k != 0.0) {
        suma[RODZAJE_REF[populacja[y]->rodzaj]] += populacja[y]->fitness;
      }
    }

    m = RODZAJE_REF[populacja[x]->rodzaj];
    for (i = 0; i < RODZAJOW; ++i) {
      if ((real) suma[m] < (real) suma[i]) m = i;
    }
  }

  if (RODZAJE[m] != populacja[x]->rodzaj) {
    fabryka::nowy(RODZAJE[m], x);
  }
}

/* SELEKCJA RODZAJOW WSZYSTKIE */

selekcja_imitacja_rodzajow_wszystkie::selekcja_imitacja_rodzajow_wszystkie(): prawd_bledu(0.0) {}
selekcja_imitacja_rodzajow_wszystkie::selekcja_imitacja_rodzajow_wszystkie(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_rodzajow_wszystkie::~selekcja_imitacja_rodzajow_wszystkie() {}

void selekcja_imitacja_rodzajow_wszystkie::wykonaj()
{
  int         x;
  vector<int> nowy_rodzaj(N);

#pragma omp parallel for private(x)
  FOREN(x) {
    if (r->rand() < prawd_bledu) {
      nowy_rodzaj[x] = r->rand(RODZAJOW);
    } else {
      int         y, i;
      vector<real> suma(RODZAJOW, 0.0);

      suma[RODZAJE_REF[populacja[x]->rodzaj]] += populacja[x]->fitness;
      FOREN(y) {
        real k = f->oblicz(x, y);
        if (k != 0.0) {
          suma[RODZAJE_REF[populacja[y]->rodzaj]] += populacja[y]->fitness;
        }
      }

      nowy_rodzaj[x] = RODZAJE_REF[populacja[x]->rodzaj];
      for (i = 0; i < RODZAJOW; ++i) {
        if ((real) suma[nowy_rodzaj[x]] < (real) suma[i]) 
          nowy_rodzaj[x] = i;
      }
    }
  }

  FOREN(x) {
    if (RODZAJE[nowy_rodzaj[x]] != populacja[x]->rodzaj) {
      fabryka::nowy(RODZAJE[nowy_rodzaj[x]], x);
    }
  }
}

/* SELEKCJA PROCES MORANA DAREK */

selekcja_proces_morana_darek::selekcja_proces_morana_darek(): prawd_bledu(0.0) {}
selekcja_proces_morana_darek::selekcja_proces_morana_darek(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_proces_morana_darek::~selekcja_proces_morana_darek() {}

void selekcja_proces_morana_darek::wykonaj()
{
  int x = r->rand(N);

  if (r->rand() < prawd_bledu) {
    fabryka::nowy(RODZAJE[r->rand(RODZAJOW)], x);
  } else {
    int y = wez_losowego_sasiada(x);

    if (y != -1) {
      real Px = populacja[x]->fitness;
      real Py = populacja[y]->fitness;
      real D = gra[1][0] - gra[0][1]; /* TODO - w SnowDrift inaczej */

      real kx = populacja[x]->stopien_wierzcholka();
      real ky = populacja[y]->stopien_wierzcholka();
      if (r->rand() < (Py/ky - Px/kx) / (D)) {
        fabryka::nowy(populacja[y]->rodzaj, x);
      }
    }
  }
}

/* SELEKCJA PROCES MORANA DAREK WSZYSTKIE */

selekcja_proces_morana_wszystkie_darek::selekcja_proces_morana_wszystkie_darek(): prawd_bledu(0.0) {}
selekcja_proces_morana_wszystkie_darek::selekcja_proces_morana_wszystkie_darek(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_proces_morana_wszystkie_darek::~selekcja_proces_morana_wszystkie_darek() {}

void selekcja_proces_morana_wszystkie_darek::wykonaj()
{
  int         x;
  vector<int> nowy_rodzaj(N);

#pragma omp parallel for private(x)
  FOREN(x) {
    if (r->rand() < prawd_bledu) {
      nowy_rodzaj[x] = RODZAJE[r->rand(RODZAJOW)];
    } else {
      int y = wez_losowego_sasiada(x);

      if (y != -1) {
        real Px = populacja[x]->fitness;
        real Py = populacja[y]->fitness;
        real D = gra[1][0] - gra[0][1]; /* TODO - w SnowDrift inaczej */
        real kx = populacja[x]->stopien_wierzcholka();
        real ky = populacja[y]->stopien_wierzcholka();

        if (r->rand() < (Py/ky - Px/kx) / (D)) {
          nowy_rodzaj[x] = populacja[y]->rodzaj;
        } else {
          nowy_rodzaj[x] = -1;
        }
      } else {
        nowy_rodzaj[x] = -1;
      }
    }
  }

  FOREN(x) {
    if (nowy_rodzaj[x] != -1) {
      fabryka::nowy(nowy_rodzaj[x], x);
    }
  }
}

/* SELEKCJA RODZAJOW II*/

selekcja_imitacja_rodzajow2::selekcja_imitacja_rodzajow2(): prawd_bledu(0.0) {}
selekcja_imitacja_rodzajow2::selekcja_imitacja_rodzajow2(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_rodzajow2::~selekcja_imitacja_rodzajow2() {}

void selekcja_imitacja_rodzajow2::wykonaj()
{
  int x = r->rand(N), m;

  if (r->rand() < prawd_bledu) {
    m = r->rand(RODZAJOW);
  } else {
    int         y, i;
    vector<real> suma(RODZAJOW, 0.0);
    vector<real> stopien(RODZAJOW, 0.0);

    suma[RODZAJE_REF[populacja[x]->rodzaj]] += populacja[x]->fitness;
    stopien[RODZAJE_REF[populacja[x]->rodzaj]] += 1.0;
#pragma omp parallel for private(y)
    FOREN(y) {
      real k = f->oblicz(x, y);
      suma[RODZAJE_REF[populacja[y]->rodzaj]] += populacja[y]->fitness * k;
      stopien[RODZAJE_REF[populacja[x]->rodzaj]] += k;
    }

    m = RODZAJE_REF[populacja[x]->rodzaj];
    for (i = 0; i < RODZAJOW; ++i) {
      if (stopien[i] > 0.0 && (real) suma[m] / stopien[m] < (real) suma[i] / stopien[i]) m = i;
    }
  }

  if (RODZAJE[m] != populacja[x]->rodzaj) {
    fabryka::nowy(RODZAJE[m], x);
  }
}

/* SELEKCJA RODZAJOW WSZYSTKIE II */

selekcja_imitacja_rodzajow2_wszystkie::selekcja_imitacja_rodzajow2_wszystkie(): prawd_bledu(0.0) {}
selekcja_imitacja_rodzajow2_wszystkie::selekcja_imitacja_rodzajow2_wszystkie(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_rodzajow2_wszystkie::~selekcja_imitacja_rodzajow2_wszystkie() {}

void selekcja_imitacja_rodzajow2_wszystkie::wykonaj()
{
  int         x;
  vector<int> nowy_rodzaj(N);

#pragma omp parallel for private(x)
  FOREN(x) {
    if (r->rand() < prawd_bledu) {
      nowy_rodzaj[x] = r->rand(RODZAJOW);
    } else {
      int         y, i;
      vector<real> suma(RODZAJOW, 0.0);
      vector<real> stopien(RODZAJOW, 0.0);

      suma[RODZAJE_REF[populacja[x]->rodzaj]] += populacja[x]->fitness;
      stopien[RODZAJE_REF[populacja[x]->rodzaj]] += 1.0;
      FOREN(y) {
        real k = f->oblicz(x, y);
        suma[RODZAJE_REF[populacja[y]->rodzaj]] += populacja[y]->fitness * k;
        stopien[RODZAJE_REF[populacja[x]->rodzaj]] += k;
      }

      nowy_rodzaj[x] = RODZAJE_REF[populacja[x]->rodzaj];
      for (i = 0; i < RODZAJOW; ++i) {
        if (stopien[i] > 0.0 && (real) suma[nowy_rodzaj[x]] / stopien[nowy_rodzaj[x]] < (real) suma[i] / stopien[i]) 
          nowy_rodzaj[x] = i;
      }
    }
  }
  
  FOREN(x) {
    if (RODZAJE[nowy_rodzaj[x]] != populacja[x]->rodzaj) {
      fabryka::nowy(RODZAJE[nowy_rodzaj[x]], x);
    }
  }
}

/* SELEKCJA RODZAJOW III */

selekcja_imitacja_rodzajow3::selekcja_imitacja_rodzajow3(): prawd_bledu(0.0) {}
selekcja_imitacja_rodzajow3::selekcja_imitacja_rodzajow3(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_rodzajow3::~selekcja_imitacja_rodzajow3() {}

void selekcja_imitacja_rodzajow3::wykonaj()
{
  int x = r->rand(N), m, sasiad;

  if (r->rand() < prawd_bledu) {
    m = r->rand(RODZAJOW);
  } else {
    real  suma = populacja[x]->fitness, p;

#pragma omp parallel for private(sasiad) reduction(+:suma)
    FOREN(sasiad) {
      real k = f->oblicz(x,sasiad);
      suma += populacja[sasiad]->fitness * k;
    }

    p = r->rand() * suma;

    sasiad = -1; suma = populacja[x]->fitness;
    while (suma < p) {
      ++ sasiad;
      real k = f->oblicz(x,sasiad);
      suma += populacja[sasiad]->fitness * k;
    }
    if (sasiad >= 0) m = RODZAJE_REF[populacja[sasiad]->rodzaj];
	else m = RODZAJE_REF[populacja[x]->rodzaj];
  }

  if (RODZAJE[m] != populacja[x]->rodzaj) {
    fabryka::nowy(RODZAJE[m], x);
  }
}

/* SELEKCJA RODZAJOW WSZYSTKIE III */

selekcja_imitacja_rodzajow3_wszystkie::selekcja_imitacja_rodzajow3_wszystkie(): prawd_bledu(0.0) {}
selekcja_imitacja_rodzajow3_wszystkie::selekcja_imitacja_rodzajow3_wszystkie(real Prawd_Bledu): prawd_bledu(Prawd_Bledu) {}
selekcja_imitacja_rodzajow3_wszystkie::~selekcja_imitacja_rodzajow3_wszystkie() {}

void selekcja_imitacja_rodzajow3_wszystkie::wykonaj()
{
  int         x;
  vector<int> nowy_rodzaj(N);

#pragma omp parallel for private(x)
  FOREN(x) {
    if (r->rand() < prawd_bledu) {
      nowy_rodzaj[x] = r->rand(RODZAJOW);
    } else {
      int         sasiad;
      real  suma = populacja[x]->fitness, p;

      FOREN(sasiad) {
        real k = f->oblicz(x,sasiad);
        suma += populacja[sasiad]->fitness * k;
      }

      p = r->rand() * suma;

      sasiad = -1; suma = populacja[x]->fitness;
      while (suma < p) {
        ++ sasiad;
        real k = f->oblicz(x,sasiad);
        suma += populacja[sasiad]->fitness * k;
      }
      if (sasiad >= 0) nowy_rodzaj[x] = RODZAJE_REF[populacja[sasiad]->rodzaj];
  
    }
  }

  FOREN(x) {
    if (RODZAJE[nowy_rodzaj[x]] != populacja[x]->rodzaj) {
      fabryka::nowy(RODZAJE[nowy_rodzaj[x]], x);
    }
  }
}


